package mgo

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"github.com/go-playground/validator/v10"
	"github.com/pkg/errors"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readpref"
	"time"
)

type MongoConfig struct {
	// 基础信息
	Host    string `json:"host" validate:"required"` // mongo 服务完整请求地址
	User    string `json:"user"`                     // 请求账号
	Pwd     string `json:"pwd"`                      // 请求密码
	MaxConn uint64 `json:"max_conn"`                 // 最大链接数，默认 100
	Timeout int64  `json:"timeout"`                  // 设置链接超时时间，单位：秒，默认 300
	// 链接加密信息
	TlsCert []byte `json:"tls_cert"` // TLS 证书文件内容
	// 数据库信息
	Database string `json:"database"` // 数据库名称
}

// BuildClient 创建 mongodb 客户端链接池
// 基础操作时，直接使用 client.Database(c.Database).Collection("name") 操作集合
// 事务操作时，使用 Client.StartSession() 开启事务操作
func (c *MongoConfig) BuildClient() (client *mongo.Client, err error) {
	// 配置校验
	if err = validator.New().Struct(c); err != nil {
		return
	}
	// 构建链接地址\最大链接数（默认100）\用户密码权限\分片
	option := options.Client().ApplyURI(c.Host)
	if c.MaxConn != 0 {
		option.SetMaxPoolSize(c.MaxConn)
	}
	if c.User != "" && c.Pwd != "" {
		option.SetAuth(options.Credential{Username: c.User, Password: c.Pwd})
	}
	if c.TlsCert != nil {
		tlsConfig := new(tls.Config)
		tlsConfig.RootCAs = x509.NewCertPool()
		if ok := tlsConfig.RootCAs.AppendCertsFromPEM(c.TlsCert); !ok {
			return nil, errors.New("")
		}
		option.SetTLSConfig(tlsConfig)
	}
	// 设置客户端连接超时时间
	if c.Timeout == 0 {
		c.Timeout = 300
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.Timeout)*time.Second)
	defer cancel()
	// 发起链接
	client, err = mongo.Connect(ctx, option)
	if err != nil {
		return
	}
	// 通过 Ping 判断主服务器是否可用
	if err = client.Ping(context.Background(), readpref.Primary()); err != nil {
		return
	}
	return
}

//func (c *MongoConfig) BuildHost() string {
//	if c.Port == 0 {
//		return c.Host
//	}
//	//if c.User != "" {
//	//	return fmt.Sprintf("mongodb://%s:%s@%s:%d", c.User, c.Pwd, c.Host, c.Port)
//	//}
//	return fmt.Sprintf("mongodb://%s:%d", c.Host, c.Port)
//}
